package com.example.springboottest.example.zookeeper;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.utils.CloseableUtils;
import org.apache.zookeeper.CreateMode;

import java.util.function.Consumer;
import java.util.function.Function;

/**
 * ZooKeeper 的命名服务主要是利用 ZooKeeper 节点的树形分层结构和子节点的顺序维护能力，来为分布式系统中的资源命名
 * <p>
 * 一、ID 生成器
 * 1、Java 的 UUID。
 * 2、分布式缓存 Redis 生成 ID：利用 Redis 的原子操作 INCR 和 INCRBY，生成全局唯一的 ID。
 * 3、Twitter 的 SnowFlake 算法。
 * 4、ZooKeeper 生成 ID：利用 ZooKeeper 的顺序节点，生成全局唯一的 ID。
 * 5、MongoDb 的 ObjectId：MongoDB 是一个分布式的非结构化 NoSQL 数据库，每插入一条记录会自动生成全局唯一的一个“_id”字段值，
 * 它是一个 12 字节的字符串，可以作为分布式系统中全局唯一的 ID。
 * <p>
 * PERSISTENT_SEQUENTIAL 持久化顺序节点。
 * EPHEMERAL_SEQUENTIAL 临时顺序节点。
 * 在创建子节点时，如果设置为上面的类型，ZooKeeper 会自动为创建后的节点路径在末尾加上一个数字，用来表示顺序。这个顺序值的最大上限就是整型的最大值。
 * <p>
 * 查看
 * get /test/IDMaker/ID-0000000029
 * <p>
 * 有以下两个方案可用于生成集群节点的编号：
 * （1）使用数据库的自增 ID 特性，用数据表存储机器的 MAC 地址或者 IP 来维护。
 * （2）使用 ZooKeeper 持久顺序节点的顺序特性来维护节点的 NodeId 编号。
 */
public class ZKCuratorIDTest {
    public static void main(String[] args) {
        String nodeName = "/test/IDMaker/ID-";
        for (int i = 0; i < 10; i++) {
            String id = makeId(nodeName);
            System.out.println("第" + i + "个创建的 id 为:" + id);
        }
    }

    static String createSeqNode(String pathPefix) {
        String zkAddress = "localhost:2183";
        // 创建一个 ZNode 顺序节点
        // 为了避免 ZooKeeper 的顺序节点暴增，建议创建之后，删除创建的节点
        return handleNode(zkAddress, client -> {
            try {
                return client.create()
                        .creatingParentsIfNeeded()
                        .withMode(CreateMode.PERSISTENT_SEQUENTIAL)
//                        .withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
                        .forPath(pathPefix);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        });
    }

    //创建 id
    public static String makeId(String nodeName) {
        String str = createSeqNode(nodeName);
        if (null == str) {
            return null;
        }
        //取得 ZooKeeper 节点的末尾序号
        int index = str.lastIndexOf(nodeName);
        if (index >= 0) {
            index += nodeName.length();
            return index <= str.length() ? str.substring(index) : "";
        }
        return str;
    }

    public static String handleNode(String zkAddress, Function<CuratorFramework, String> function) {
        CuratorFramework client = ClientFactory.createSimple(zkAddress);
        try {
            client.start();

            return function.apply(client);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            CloseableUtils.closeQuietly(client);
        }
        return null;
    }
}
